<?php
/**
 *
 *
 * @package default
 * @author Alexander Sibiryakov
 **/
$this->useClass("Model_AclAssistant");
class Model_AclAssistantPlatonica extends Model_AclAssistant
{

	/**
	 * Выводит список доступных действий у текущего пользователя и его роли по отношению к оъекту $oid.
	 * Если $oid не передан, то система пытается его определить основываясь на данных get запроса.
	 *
	 * @return array
	 * @author Alexander Sibiryakov
	 **/
	public function getAvailableOperations( $oid=NULL )
	{
		$operations = array();
		if ($oid == NULL) {
			if ( isset($this->acl_data['id_item']) )  {
				$oid = $this->acl_data['id_item'];
			}	else {
				$oid = $this->acl_data['comity_oid'];
			}
		}

		$ops = $this->acl_instance->getRights(
			$oid,
			array(
				$this->acl_data['user_id'],
				$this->acl_data['role']['oid'],
			)
		);
		$operations = $this->getRightsPostprocess($ops);

		$this->available_operations = $operations;
		return $operations;
	}

	/**
	 * Отдаст массив следующего вида
	 * 	[0]=>array(7) {  ["id"]=>"12" ["parent_id"]=>"11" ["lft"]=>"20" ["rgt"]=>"21" ["comment"]=>"Role administrator"	["var"]=>string(0) "" ["id_object"]=>"6" }
	 *
	 * @return void
	 * @author Alexander Sibiryakov
	 **/
	public function getRolesList( $community_oid ) {

		if (!isset($this->community_roles[ $community_oid ])) {
			$ai = $this->acl_instance;
			// выдернем сообщество
			$community_roles_root = $ai->getPathWCond(
				array(
					"1" => array("id_object" => $community_oid),
					"2" => array("id_object" => 0 /* roles */)
				),
				"t_subject_relations", "id", "parent_id",
				2
			);
			// выдернем роли для сообщества
			$this->community_roles[ $community_oid ] = $ai->getLevelChildsById($community_roles_root['id']);
			return $this->community_roles[ $community_oid ];
		} else return $this->community_roles[ $community_oid ];
	}

	/**
	 * Вернет массив с информацией о роли и по ключу users, информация о юзерах в этой роли
	 *
	 * @param $community_oid ид сообщества
	 * @return array
	 * @author Alexander Sibiryakov
	 **/
	public function getRolesUserList( $community_oid )	{
		$ai = $this->acl_instance;
		$community_roles = $this->getRolesList( $community_oid );

		foreach ($community_roles as $k=>$role) {
			$community_roles[$k]['users'] = $ai->getLevelChildsById($role['id']);
		}
		return $community_roles;

	}

	/**
	 * Вернет массив с ид-шниками пользователей в $community_oid с ролью $role_name
	 *
	 * @param $community_oid ид сообщества
	 * @param $role_name алиас роли
	 * @return array
	 * @author Alexander Sibiryakov
	 **/
	public function getUsersByRoleAlias( $community_oid, $role_name )	{
		$role_oid = $this->regfind($role_name."_oid");
		$ai = $this->acl_instance;
		$community_roles = $this->getRolesList( $community_oid );
		$users = NULL;
		foreach ($community_roles as $k=>$role) {
			if ($role['id_object']==$role_oid) {
				$usr = $ai->getLevelChildsById($role['id']);
				foreach ($usr as $k=>$v) {
					$users[] = $v['id_object'];
				}
				break;
			}

		}
		return $users;

	}

	/**
	 * Вернет массив с ид-шниками пользователей для корневого узла с ролью $role_name
	 *
	 * @param $role_name алиас роли
	 * @return array
	 * @author Alexander Sibiryakov
	 **/
	public function getNodeUsersByRoleAlias( $role_name )	{
		$role_oid = $this->regfind($role_name."_oid");
		$ai = $this->acl_instance;

		if (!$role_oid) return false; // не возможно найти роль по алиасу

		$role = $ai->getPathWCond( // ищем заданную роль в ролях узла
			array(
				"1" => array("id_object" => 0 /* roles of node */),
				"2" => array("id_object" => $role_oid /* role_oid */)
			),
			"t_subject_relations", "id", "parent_id",
			2
		);

		if (!$role) return false;

		$users = NULL;

		$usr = $ai->getLevelChildsById($role['id']);
		foreach ($usr as $k=>$v) {
			$users[] = $v['id_object'];
		}

		return $users;

	}


	public function createItem( $id_object, $parent_oid ) {
		$parent_node = $this->acl_instance->getAclTreeNode($parent_oid);

		if ( (isset($parent_node)) && is_array($parent_node) ) {
			$this->acl_instance->_addAclTreeNode( NULL, $parent_node['id'], $id_object, "New item" );
			return true;
		} else {
			return false;
		}
		return false;
	}

	public function createComment( $id_object, $parent_oid ) {
		return $this->createItem($id_object, $parent_oid);
	}

	/**
	 * Возвращает уровень на котором находится комментарий, считая что уровень итема нулевой.
	 *
	 * @param $item_oid, $comment_oid int - ид итема и ид комментария, уровень которого нужно получить
	 * @return int
	 * @author Alexander Sibiryakov
	 **/
	public function getCommentLevel( $item_oid, $comment_oid ) {
		if ($comment_oid==0) return 0;
		return $this->acl_instance->getDepthBtwNodes($item_oid, $comment_oid);
	}

	/**
	 * Добавить нового пользователя на узле
	 *
	 * @param $user_oid ид юзера
	 * @param $full_name полное имя
	 * @param $role_name алиас роли
	 * @return int ид записи в таблице субъектов
	 * @author Alexander Sibiryakov
	 **/
	public function addNewUser($user_oid, $full_name, $role_name) {
		// --- acl --- {{{
		$role_srid = $this->regfind($role_name.'_srid');
		if (isset($role_srid)) {
			// тут наверное нужно в субъекты узла записать
			return $this->acl_instance->_addSubjectRelation( NULL, $role_srid, $user_oid , 'User '.$full_name );
		} else {
			error (__METHOD__.' role "'.$role_name.'" not found');
			// FIXME : 2007-11-23 : lucky
			// вызвать rollback()
			return false;
		}
	}

	/**
	 * Добавит новую роль в нод. Эта роль не появится в сообществах, ее нужно будет как то добавить руками.
	 *
	 * @param $role_oid ид роли
	 * @param $name алиас роли
	 * @return int ид роли
	 * @author Alexander Sibiryakov
	 **/
	public function addRoleIntoNode( $role_oid, $name )	{
		// тут еще добавление в субъекты
		$rn_id = $this->acl_instance->_addSubjectRelation( NULL,  $this->regfind("node_role_node_srid"), $role_oid, 'Role '.$name );

		$this->regstore($name."_srid", $rn_id);
		$this->regstore($name."_oid", $role_oid);

		return $role_oid;
	}

	/**
	 * создаст узел и создаст в нем дефолтные роли, назначит админа
	 *
	 * @param $node_oid - ид узла
	 * @return void
	 * @author Alexander Sibiryakov
	 **/
	public function CreateNode( $node_oid ) {

		/*
		Создание нода:
		0. создать нод
		1. создать нод в таблице отношений
		2. создать дефолтные роли для нода
		3. прописать права этим дефолтным ролям
		4. создать аккаунт администратора узла
		5. прописать ему права
	 	*/

		$idat    = $this->acl_instance->_addAclTreeNode( NULL, NULL, $node_oid, 'Node of the World!' );
		$id_node = $this->acl_instance->_addSubjectRelation( NULL, NULL, $node_oid, 'Node of the World!' );
		$this->regstore("node_idat", $idat);

		// добавим его в отношения субъектов
		$id_role_node = $this->acl_instance->_addSubjectRelation( NULL, $id_node, 0, "Roles of World" );

		$this->regstore("node_oid", $node_oid);
		$this->regstore("node_srid", $id_node);
		$this->regstore("node_role_node_srid", $id_role_node);

	}

	/**
	 * Создает раздел
	 *
	 * @param $community_oid ид сообщества
	 * @param $oid ид свежесозданного раздела
	 * @param $cname название раздела, для комментария
	 * @return void
	 * @author Alexander Sibiryakov
	 **/
	public function createTdi( $community_oid, $oid, $cname=NULL ) {
		$comm_subj = $this->acl_instance->getSubjectByOid($community_oid);

		$id_relation = $this->acl_instance->_addSubjectRelation( NULL, $comm_subj['id'], $oid, $cname );

		$atn = $this->acl_instance->getAclTreeNode( $community_oid );
		$this->acl_instance->_addAclTreeNode( NULL, $atn['id'], $oid , "Tdi ".$cname );
	}

	/**
	 * Создает сообщество в смысле добавляет узлы в дерево прав и отношений, а также ставит дефолтные ACL
	 *
	 * @param $oid ид сообщества
	 * @param $cname название раздела, для комментария
	 * @return int - кол-во добавленных ролей или false, если ничего не получилось
	 * @author Alexander Sibiryakov
	 **/
	public function createCommunity( $oid, $cname ) {
		// добавим его в отношения субъектов
		$id_relation = $this->acl_instance->_addSubjectRelation( NULL, $this->regfind("node_srid"), $oid, $cname );

		// добавим ему узел "Роли"
		$id_role_node = $this->acl_instance->_addSubjectRelation( NULL, $id_relation, 0, "Roles of ".$cname );

		// теперь добавим в отношения субъектов все роли, которые есть на узле
		$roles = $this->acl_instance->getLevelChildsById($this->regfind("node_role_node_srid"));
		foreach ($roles as $key=>$role) {
			if ($this->acl_instance->_addSubjectRelation( NULL, $id_role_node, $role['id_object'], $role['comment'] ) > 0) {
				$added++;
				$role_ids[]=$role['id_object'];
			}
		}

		if ($added > 0) {
			// поставить акл с дефолтными настройками
			$node_idat = $this->regfind("node_idat");
			$this->acl_instance->_addAclTreeNode( NULL, $node_idat , $oid, "New community: ".$cname );
			$this->acl_instance->_copyAclList( $role_ids, $node_idat, $oid );

			return $added;
		}
		return false;

	}

	/**
	 * Добавляет пользователя в сообщество
	 *
	 * @param $community_oid ид сообщества
	 * @param $role_oid идентификатор роли, в качестве которой нужно добавить пользователя
	 * @param $user_oid идентификатор юзера
	 * @return int - ид-шник по таблице отношений субъектов или false
	 * @author Alexander Sibiryakov
	 **/
	public function joinCommunity($community_oid, $role_oid, $user_oid) {
		// выберем id роли в t_subject_relations
		$result = $this->acl_instance->getPathWCond( array(
			"1"=>array("id_object"=>$this->regfind("node_oid")),
			"2"=>array("id_object"=>$community_oid),
			"4"=>array("id_object"=>$role_oid)
			 ), "t_subject_relations", "id", "parent_id", 4
			);

		if ($result == false) return false;

		return $this->acl_instance->_addSubjectRelation( NULL, $result['id'], $user_oid, $user_oid.' joined' );
	}

	/**
	 * Удаляет пользователя из сообщества
	 *
	 * @param $community_oid ид сообщества
	 * @param $user_oid идентификатор юзера
	 * @return boolean - получилось/не получилось
	 * @author Alexander Sibiryakov
	 **/
	public function leaveCommunity($community_oid, $user_oid) {
		$ai    = $this->acl_instance;

		$user_role = $ai->getPathWCond (
			array(
				"1" => array("id_object" => $community_oid),
				"2" => array("id_object" => 0 /* roles */),
				"4" => array("id_object" => $user_oid)
			),
			"t_subject_relations", "id", "parent_id",
			4
		);
		if ($user_role['id']) { // есть роль
			/* выводит содержимое nested set в виде дерева
			function aaa($a) {
				foreacH($a->ctx->db->query('select * from t_subject_relations order by lft') as $row) {
					echo str_repeat(" ", $row['parent_id']) . sprintf("%4s [%2s %2s] %s", $row['id'], $row['lft'], $row['rgt'], "{$row['comment_create']}\n");
				}
			}
			 /*/
			//var_dumP($user_role['id'], $role);
			//aaa($this);
			$status = $ai->removeSubjectRelation($user_role['id']);
			//aaa($this);
			return true;
		}
		return false;
	}

    /**
	 * Изменить роль пользователя в сообществе
	 *
	 * @param oid $user_oid $oid пользователя
	 * @param string $role_name $алиас роли
	 * @param oid $community_oid $oid сообщества
	 * @access public
	 * @return boolean
	*/
	public function setRoleByOid($community_oid, $user_oid, $role_oid)	{
		if (!isset($community_oid) || !isset($user_oid) || empty($role_oid))
		{
			error(__METHOD__."($community_oid, $user_oid, $role_oid)");
			return false;
		}

		$user  = NULL;

		$ai    = $this->acl_instance;
		if (!isset($community_oid)) $community_oid = $this->acl_data['comity_oid'];

		// найти юзера по oid
		// понять текущую роль юзера сообщества в дереве субъектов
		// если роли отличаются:
		//   найти новую роль юзера в сообщества в дереве субъектов
		//   перемесить его.

		// поъехали
		// ищем юзвера в ролях сообщества
		$user_node = $ai->getPathWCond(
			array(
				"1" => array("id_object" => $community_oid),
				"2" => array("id_object" => 0 /* roles of community */),
				"4" => array("id_object" => $user_oid /* user_oid */)
			),
			"t_subject_relations", "id", "parent_id",
			4
		);

		if ($role_oid)
		{
			// ищем заданную роль в ролях сообщества
			$new_role = $ai->getPathWCond(
				array(
					"1" => array("id_object" => $community_oid),
					"2" => array("id_object" => 0 /* roles of community*/),
					"3" => array("id_object" => $role_oid /* role_oid */)
				),
				"t_subject_relations", "id", "parent_id",
				3
			);

			if ($new_role && $user_node['parent_id'] !== $new_role['id']) { // если роль не та же самая
						/* выводит содержимое nested set в виде дерева
						function aaa($a) {
							foreacH($a->ctx->db->query('select * from t_subject_relations order by lft') as $row) {
								echo str_repeat(" ", $row['parent_id']) . sprintf("%4s [%2s %2s] %s", $row['id'], $row['lft'], $row['rgt'], "{$row['comment_create']}\n");
							}
						}
						/*/
				$status = $ai->moveSubjectRelation($user_node, $new_role);
				return $role_oid;
			}
		}
		return false;
	} // end of function setRoleByOid

	/**
	 * Обертка для setRoleByOid
	 *
	 * @return void
	 * @author Alexander Sibiryakov
	 **/
	public function setRoleByAlias($community_oid, $user_oid, $role_name) {
		$role_oid = $this->regfind($role_name."_oid");
		return $this->setRoleByOid($community_oid, $user_oid, $role_oid);
	}


	//!an accessor
	/**
	 * Вернуть количество пользователей указанного сообщества.
	 *
	 * FIXME : 2007-11-10 : lucky
	 * считает для всех ролей без разбора. может попадутся анонимы...
	 *
	 * @param mixed $community_oid oid сообщества
	 * @access public
	 * @return integer количество участников сообщества.
	*/
	public function getMembersCount ($community_oid)	{
		// считаем что структура дерева отношений
		// ... сообщество -> корень для ролей -> [роль] -> [члены]
		// считаем всех членов, которые к тому же юзверы
		// (класс объектов user)

		$ai = $this->acl_instance;
		// корень всех ролей
		$community_roles_root = $ai->getPathWCond(
			array(
				"1" => array("id_object" => $community_oid),
				"2" => array("id_object" => 0 /* roles */)
			),
			"t_subject_relations", "id", "parent_id",
			2
		);

		$node_id = $community_roles_root;
		$db = $this->db;
		$tname = "t_subject_relations";
		$t_users_name = "store_class_user";
		// определим структуру данных с инфой о перемещаемом узле
		if (is_array($node_id)) { // нам передали готовую структуру
			$node = $node_id;
		} else { // найдем узел
			$sql = "SELECT ".al_node.",".al_parent.",".ns_lft.",".ns_rgt." FROM ".$tname
				." WHERE ".al_node." = ".$db->quote($node_id);
			$node = $db->QueryOne( $sql );
		}
		$sql = "SELECT COUNT(DISTINCT U.oid) as `count` FROM ".$tname." R"
			." INNER JOIN ".$t_users_name." U ON (R.id_object = U.oid)"
			." WHERE R.".ns_lft." > ".$db->quote($node[ns_lft])
			.  " AND R.".ns_rgt." < ".$db->quote($node[ns_rgt])
			.  " AND R.".al_parent." <> ".$db->quote($node[al_node])
			;
		$rs = $db->QueryOne( $sql );
		$count = $rs['count'];
		return $count;
	} // end of function getMembersCount

    /**
	 * Изменить роль пользователя на УЗЛЕ
	 *
	 * @param oid $user_oid $oid пользователя
	 * @param string $role_name алиас роли пользователя
	 * @access public
	 * @return boolean
	*/
	public function changeRoleNode($user_oid, $role_name)	{
		if (!isset($user_oid) || empty($role_name))
		{
			error(__METHOD__."($user_oid, $role_name)");
			return 1; // кривые входные параметры
		}

		$role_oid = $this->regfind($role_name."_oid");

		if (!$role_oid) return 2; // не возможно найти роль по алиасу

		$ai    = $this->acl_instance;

		$user_node = $ai->getPathWCond( // тут нужно искать путь в ролях узла
			array(
				"1" => array("id_object" => 0 /* roles of node */),
				"3" => array("id_object" => $user_oid /* user_oid */)
			),
			"t_subject_relations", "id", "parent_id",
			3
		);

		$new_role = $ai->getPathWCond( // ищем заданную роль в ролях узла
			array(
				"1" => array("id_object" => 0 /* roles of node */),
				"2" => array("id_object" => $role_oid /* role_oid */)
			),
			"t_subject_relations", "id", "parent_id",
			2
		);

		if ($new_role && $user_node['parent_id'] !== $new_role['id']) { // если роль не та же самая
				$status = $ai->moveSubjectRelation($user_node, $new_role);
				if ($status === true) return 0; // все в порядке
				else return 3; // не ясно что не в порядке
		} else {
			return 4; // пользователь уже имеет ту роль которую ему хотят назначить
		}
	} // end of function changeRoleNode


} // END class
